package chagine.core.config;

import chagine.core.util.DateUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author zevin
 * @date 2023/3/6 20:43
 */
public class AsyncMonitor {

    private TaskExecutor taskExecutor;

    /**
     * 用于加载【taskExecutor】（通过【AsyncConfigurer】配置的taskExecutor没有注册到Spring容器）
     */
    public static ThreadPoolTaskExecutor THREAD_POOL_TASK_EXECUTOR;

    @PostConstruct
    public void init(){
        //set->taskExecutor
        taskExecutor = THREAD_POOL_TASK_EXECUTOR;

        //初始化【threadPoolAsyncTaskPerformSequencing】
//        for (int i = 1; i <= THREAD_POOL_TASK_EXECUTOR.getMaxPoolSize(); i++) {
//            threadPoolAsyncTaskPerformSequencing.put(THREAD_POOL_TASK_EXECUTOR.getThreadNamePrefix() + "-" +i, new AsyncTaskSteps(ASYNC_TASK_STEP_CAPACITY));
//        }

        //初始化【task】
        if(AsyncTaskConfig.ASYNC_TASK_LOG_SELECTION == 3) taskContainer = new ConcurrentHashMap<>(ASYNC_TASK_CAPACITY);

        //清除pointer
        THREAD_POOL_TASK_EXECUTOR = null;
    }


    /**
     * 获取异步相关统计信息
     */
    //获取异步任务的统计信息
    public List<AsyncInfo> getStatisticsForAsyncTask() {
        List<AsyncInfo> asyncInfoList = CollectionUtil.newArrayList();
        Object[] thread = {taskExecutor};

        //spring异步框架
        if (taskExecutor instanceof ThreadPoolTaskExecutor) {
            for (Object t : thread) {
                //ThreadPoolTaskExecutor
                ThreadPoolTaskExecutor springTaskExecutor = (ThreadPoolTaskExecutor) t;
                //ThreadPoolExecutor
                ThreadPoolExecutor tpe = springTaskExecutor.getThreadPoolExecutor();

                asyncInfoList.add(new AsyncInfo()
                        //ThreadPoolExecutor任务相关参数
                        .setTaskCount(tpe.getTaskCount())//提交任务数
                        .setCompletedTaskCount(tpe.getCompletedTaskCount())//完成任务数
                        .setTpeActiveCount(tpe.getActiveCount())//执行中任务数
                        .setQueueSize(tpe.getQueue().size())//剩余任务数
                        .setQueueRemainingCapacity(tpe.getQueue().remainingCapacity())//当前可用队列长度
                        .setRejectedExecutionHandler(tpe.getRejectedExecutionHandler().getClass().getSimpleName())//任务拒绝策略名称
                );
            }
        }

        return asyncInfoList;
    }
    //获取spring异步线程的统计信息
    public List<AsyncInfo> getStatisticsForSpringAsync() {
        List<AsyncInfo> asyncInfoList = CollectionUtil.newArrayList();
        Object[] thread = {taskExecutor};

        for (Object t : thread) {
            //ThreadPoolTaskExecutor
            ThreadPoolTaskExecutor springTaskExecutor = (ThreadPoolTaskExecutor) t;
            //ThreadPoolExecutor
            ThreadPoolExecutor tpe = springTaskExecutor.getThreadPoolExecutor();

            asyncInfoList.add(new AsyncInfo()
                    //ThreadPoolTaskExecutor
                    .setMaxPoolSize(springTaskExecutor.getMaxPoolSize())
                    .setPoolSize(springTaskExecutor.getPoolSize())
                    .setCorePoolSize(springTaskExecutor.getCorePoolSize())
                    .setActiveCount(springTaskExecutor.getActiveCount())
                    .setKeepAliveSeconds(springTaskExecutor.getKeepAliveSeconds())
                    .setThreadGroupName(Optional.ofNullable(springTaskExecutor.getThreadGroup()).map(ThreadGroup::getName).orElse(null))
                    .setThreadNamePrefix(springTaskExecutor.getThreadNamePrefix())
                    .setThreadPriority(springTaskExecutor.getThreadPriority())
                    //ThreadPoolExecutor
                    .setTpePoolSize(tpe.getPoolSize())
                    .setTpeActiveCount(tpe.getActiveCount())
                    .setTpeCorePoolSize(tpe.getCorePoolSize())
                    .setLargestPoolSize(tpe.getLargestPoolSize())
                    .setMaximumPoolSize(tpe.getMaximumPoolSize())
                    .setKeepAliveTime(tpe.getKeepAliveTime(TimeUnit.SECONDS))
                    //ThreadPoolExecutor-task
                    .setTaskCount(tpe.getTaskCount())
                    .setCompletedTaskCount(tpe.getCompletedTaskCount())
                    .setQueueSize(tpe.getQueue().size())
                    .setQueueRemainingCapacity(tpe.getQueue().remainingCapacity())
                    .setRejectedExecutionHandler(tpe.getRejectedExecutionHandler().getClass().getSimpleName())
            );
        }

        return asyncInfoList;
    }
    @Deprecated
    public void printInfo() {
//            System.out.println();
//            System.out.println();
//            System.out.println("—————————————————————————————————————");
//            System.out.println("提交任务数"+tpe.getTaskCount());
//            System.out.println("完成任务数"+tpe.getCompletedTaskCount() );
//            System.out.println("当前有"+tpe.getActiveCount()+"个线程正在处理任务");
//            System.out.println("还剩"+tpe.getQueue().size()+"个任务");
//            System.out.println("—————————————————————————————————————");
//            map.put("提交任务数-->",threadPoolExecutor.getTaskCount());
//            map.put("完成任务数-->",threadPoolExecutor.getCompletedTaskCount());
//            map.put("当前有多少线程正在处理任务-->",threadPoolExecutor.getActiveCount());
//            map.put("还剩多少个任务未执行-->",threadPoolExecutor.getQueue().size());
//            map.put("当前可用队列长度-->",threadPoolExecutor.getQueue().remainingCapacity());
//            map.put("当前时间-->", DateUtil.getToday());
    }

    /**
     * 异步相关统计信息
     */
    @Data
    @Accessors(chain = true)
    public static class AsyncInfo {
        //ThreadPoolTaskExecutor相关信息
        private int maxPoolSize;//最大线程数
        private int poolSize;//线程数
        private int corePoolSize;//核心线程数
        private int activeCount;//活跃线程数
        private int keepAliveSeconds;//保持活跃秒数

        private String threadGroupName;//线程组名称
        private String threadNamePrefix;//线程名称前缀
        private int threadPriority;//线程优先级

        //ThreadPoolTaskExecutor-ThreadPoolExecutor相关信息
        private int largestPoolSize;//池中同时存在的最大线程数
        private int maximumPoolSize;//允许的最大线程数
        private int tpePoolSize;//线程数
        private int tpeCorePoolSize;//核心线程数
        private int tpeActiveCount;//活跃线程数
        private long keepAliveTime;//保持活跃秒数

        //任务相关参数
        private long taskCount;//提交任务数
        private long completedTaskCount;//完成任务数
        private int queueSize;//剩余任务数
        private int queueRemainingCapacity;//可提交任务数
        private String rejectedExecutionHandler;//拒绝任务处理器

        @Override
        public String toString() {
            return "AsyncInfo:{" +
                    " \n 最大线程数=" + maxPoolSize +
                    ",\n 线程数=" + poolSize +
                    ",\n 核心线程数=" + corePoolSize +
                    ",\n 活跃线程数=" + activeCount +
                    ",\n 保持活跃秒数=" + keepAliveSeconds +
                    ",\n 线程组名称='" + threadGroupName + '\'' +
                    ",\n 线程名称前缀='" + threadNamePrefix + '\'' +
                    ",\n 线程优先级=" + threadPriority +
                    ",\n 池中同时存在的最大线程数=" + largestPoolSize +
                    ",\n 允许的最大线程数=" + maximumPoolSize +
                    ",\n tpe线程数=" + tpePoolSize +
                    ",\n tpe核心线程数=" + tpeCorePoolSize +
                    ",\n tpe活跃线程数=" + tpeActiveCount +
                    ",\n 保持活跃秒数=" + keepAliveTime +
                    ",\n 提交任务数=" + taskCount +
                    ",\n 完成任务数=" + completedTaskCount +
                    ",\n 剩余任务数=" + queueSize +
                    ",\n 可提交任务数=" + queueRemainingCapacity +
                    ",\n 拒绝任务处理器='" + rejectedExecutionHandler + '\'' +
                    '}';
        }

        public String syncInfo(){
            return this.toString();
        }

        public String allTaskInfo() {
            return "taskInfo{" +
                    "\n" + "执行中任务数=" + tpeActiveCount +
                    ",\n" + "提交任务数=" + taskCount +
                    ",\n" + "完成任务数=" + completedTaskCount +
                    ",\n" + "剩余任务数=" + queueSize +
                    ",\n" + "可提交任务数=" + queueRemainingCapacity +
                    ",\n" + "拒绝任务处理器=" + rejectedExecutionHandler + "'" +
                    "\n}";
        }
    }



    /**
     * ————————————————————————————————————
     * 线程池异步任务执行排序（暂不使用）
     * <异步线程id, 异步任务执行信息>
     *  ————————————————————————————————————
     */
    private final Map<String, AsyncTaskSteps> threadPoolAsyncTaskPerformSequencing = new HashMap<>();
    private final int ASYNC_TASK_STEP_CAPACITY = 100;

    private void addStep(String threadName, Long requestId, AsyncTaskStatusEnum status, LocalDateTime now) {
        threadPoolAsyncTaskPerformSequencing.get(threadName).add(new AsyncTaskStep(requestId, status, now));
    }

    public static class AsyncTaskSteps {
        transient AsyncTaskStep[] elementData;
        private final int capacity;
        private int size;
        private int readPos;

        public AsyncTaskSteps(int capacity){
            this.capacity = capacity;
        }

        public void add(AsyncTaskStep data){
            elementData[(size++)%capacity] = data;
        }

        public AsyncTaskStep[] read(){
            if(size > capacity){
                final AsyncTaskStep[] arr = new AsyncTaskStep[capacity];
                System.arraycopy(elementData, size, arr, 0, capacity-size+1);
                System.arraycopy(elementData, 0, arr, capacity-size, size);
                return arr;
            }else {
                return elementData;
            }
        }
    }

    /**
     * 异步任务执行信息
     */
    @Data
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class AsyncTaskStep{
        private Long requestId;
        private AsyncTaskStatusEnum asyncTaskStatusEnum;
        private LocalDateTime time;
    }



    /**
     * ————————————————————————————————————
     * 任务相关
     * ————————————————————————————————————
     */
    private Map<Long, AsyncTaskInfo> taskContainer;
    private final int ASYNC_TASK_CAPACITY = 64;

    //初始化任务
    public AsyncTaskInfo createTask(Long requestId, Method asyncMethod) {
        //        addStep(threadName, requestId, status, now);

        AsyncTaskInfo asyncTaskInfo = new AsyncTaskInfo()
                .setRequestId(requestId)
                .setStatus(AsyncTaskStatusEnum.STARTED)
                .setStartTime(System.currentTimeMillis())
                .setAsyncMethod(asyncMethod);
        setTaskInfo(asyncTaskInfo);
        return asyncTaskInfo;
    }

    public void submitFailedTask(AsyncTaskInfo asyncTaskInfo){
        asyncTaskInfo
                .setStatus(AsyncTaskStatusEnum.FAILED)
                .setEndTime(System.currentTimeMillis());
        setTaskInfo(asyncTaskInfo);
    }

    public void submitSuccessTask(AsyncTaskInfo asyncTaskInfo){
        if(asyncTaskInfo.getStatus() != AsyncTaskStatusEnum.FAILED)
        asyncTaskInfo.setStatus(AsyncTaskStatusEnum.SUCCESS);

        asyncTaskInfo.setEndTime(System.currentTimeMillis());
        setTaskInfo(asyncTaskInfo);
    }

    //set->单条任务信息
    public void setTaskInfo(AsyncTaskInfo asyncTaskInfo){
        if(taskContainer.size() < ASYNC_TASK_CAPACITY){
            taskContainer.put(asyncTaskInfo.getRequestId(), asyncTaskInfo);
        }
    }

    //get->单条任务信息
    public AsyncTaskInfoVo getTaskInfo(Long requestId, boolean clearTask){
        AsyncTaskInfo asyncTaskInfo = clearTask ? this.taskContainer.remove(requestId) : this.taskContainer.get(requestId);
        return asyncTaskInfo != null
                ? AsyncTaskInfoVo.converterBy(asyncTaskInfo)
                : this.taskContainer.size() >= ASYNC_TASK_CAPACITY
                ? AsyncTaskInfoVo.taskIsFill(requestId)
                : AsyncTaskInfoVo.unknownCause(requestId)
                ;
    }

    /**
     * 异步任务信息
     * @author zevin
     * @date 2023/3/8 0:35
     */
    @Data
    @Accessors(chain = true)
    @NoArgsConstructor
    @AllArgsConstructor
    public static class AsyncTaskInfoVo {
        private Long requestId;
        private String status;
        private LocalDateTime startTime;
        private LocalDateTime endTime;
        private String executeTime;
        private String asyncMethod;

        private String attachInformation;

        public static AsyncTaskInfoVo converterBy(AsyncTaskInfo asyncTaskInfo){
            Long requestId = asyncTaskInfo.getRequestId();
            String status =asyncTaskInfo.getStatus().getStateInfo();
            LocalDateTime startTime = DateUtil.getLocalDateTimeByTimestampMills(asyncTaskInfo.getStartTime());
            LocalDateTime endTime = DateUtil.getLocalDateTimeByTimestampMills(asyncTaskInfo.getEndTime());
            String executeTime = cn.hutool.core.date.DateUtil.formatBetween(ChronoUnit.MILLIS.between(startTime, endTime));
            String asyncMethod = stitchCompletedMethodName(asyncTaskInfo.getAsyncMethod());
            String attachInformation = "null";
            return new AsyncTaskInfoVo(requestId, status, startTime, endTime, executeTime, asyncMethod, attachInformation);
        }

        public static AsyncTaskInfoVo unknownCause(Long requestId){
            return new AsyncTaskInfoVo()
                    .setRequestId(requestId)
                    .setAttachInformation("查询不到任务，造成的原因可能如下: " +
                            "1.【记录任务】日志策略没有开启" +
                            "2.发布异步任务异常" +
                            "3.异步任务已清空"
                    );
        }

        public static AsyncTaskInfoVo taskIsFill(Long requestId) {
            return new AsyncTaskInfoVo()
                    .setRequestId(requestId)
                    .setAttachInformation("查询不到任务，记录任务的队列已满!");
        }
    }

    /**
     * 异步任务信息
     * @author zevin
     * @date 2023/3/8 0:35
     */
    @Data
    @Accessors(chain = true)
    public static class AsyncTaskInfo {
        private Long requestId;
        private AsyncTaskStatusEnum status;
        private long startTime;
        private long endTime;
        private long executeTime;
        private Method asyncMethod;

        private String attachInformation;

        public AsyncTaskInfo(){}

        public AsyncTaskInfo(String attachInformation){
            this.attachInformation = attachInformation;
        }
    }
    /**
     * 任务状态枚举
     */
    public enum AsyncTaskStatusEnum {
        STARTED(0, "任务已经启动"),
        RUNNING(1, "任务正在运行"),
        SUCCESS(2, "任务执行成功"),
        FAILED(-1, "任务执行失败");
        private final int state;
        private final String stateInfo;

        AsyncTaskStatusEnum(int state, String stateInfo) {
            this.state = state;
            this.stateInfo = stateInfo;
        }

        public int getState() {
            return state;
        }

        public String getStateInfo() {
            return stateInfo;
        }
    }


    /**
     * 工具类方法
     */
    //获取类名
    private static String getClassName(Class... classes) {
        return (classes == null || classes.length == 0) ? "null" : Arrays.stream(classes).map(Class::getSimpleName).collect(Collectors.joining(", "));
    }
    //拼接方法完整名称
    private static String stitchCompletedMethodName(Method asyncMethod) {
        return getClassName(asyncMethod.getDeclaringClass()) + "." + asyncMethod.getName() + "(" + getClassName(asyncMethod.getParameterTypes()) + ")";
    }
}